<?php

namespace RedMVC\Http;
use RedMVC;

/**
 * Request class
 *
 * @category    RedMVC
 * @author      Jan Fischer, bitWorking <info@bitworking.de>
 */
class Request{

    const PARAM_TYPE_STRING = 'string';
    const PARAM_TYPE_INT    = 'int';
    const PARAM_TYPE_FLOAT  = 'float';
    const PARAM_TYPE_BOOL   = 'bool';
    const PARAM_TYPE_OBJECT = 'object';

    protected $controllerName;
    protected $actionName;
    protected $params = array();
    protected $requestString;
    protected $baseUrl;
    protected $rootUrl;
    protected $_isSSL = false;
        
    public function __construct(){
        $this->extractParams();
    }

    /**
     * extracts baseUrl, rootUrl and uses the router from config to
     * read the controller name, action name, GET params and request string
     */
    protected function extractParams(){
        
        $config = RedMVC\Registry::get('RedMVC_Config');

        $pathInfo       = pathinfo($_SERVER['SCRIPT_NAME']);
        $dirName        = $pathInfo['dirname'];
        $dirName        = str_replace('/index.php', '', $dirName);
        $requestUri     = $_SERVER['REQUEST_URI'];
        
        $this->baseUrl  = rtrim($dirName, '/');
        
        $protocol = 'http';
        if(isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] != 'off'){
            $protocol       = 'https';
            $this->_isSSL   = true;
        }
        $this->rootUrl  = $protocol.'://'.$_SERVER['SERVER_NAME'].$this->baseUrl;
    
        RedMVC\Registry::set('RedMVC_BaseUrl', $this->baseUrl);
        RedMVC\Registry::set('RedMVC_RootUrl', $this->rootUrl);

        // router
        $routerClass = $config->router;
        if(!class_exists($routerClass)){
            $routerClass = 'RedMVC\Router\Standard';
        }
        $router = new $routerClass;
        RedMVC\Registry::set('RedMVC_Router', $router);
        RedMVC\Event\Dispatcher::fire(RedMVC\Event\Dispatcher::EVENT_ROUTER_INIT, array($router));
        $router->readUrl();
   
        $this->controllerName   = $router->controllerName;
        $this->actionName       = $router->actionName;
        $this->params           = $router->params;
        $this->requestString    = $router->requestString;
    }
    
    public function getControllerName(){
        return $this->controllerName;
    }
    
    public function getActionName(){
        return $this->actionName;
    }
    
    public function getParams(){
        return $this->params;
    }
    
    public function getBaseUrl(){
        return $this->baseUrl;
    }

    public function getRootUrl(){
        return $this->rootUrl;
    }

    public function getRequestString(){
        return $this->requestString;
    }
    
    public function __get($key){
        if(array_key_exists($key, $this->params)){
            return $this->params[$key];
        }
        return null;
    }
    
    public function __isset($key){
        return array_key_exists($key, $this->params);
    }
    
    public function __unset($key){
        unset($this->params[$key]);
    }
    
    protected function _typeCast($value, $type){
        if(null === $value){
            return null;
        }
        else if($type == self::PARAM_TYPE_INT){
            return (int) $value;
        }
        else if($type == self::PARAM_TYPE_FLOAT){
            return (float) $value;
        }
        else if($type == self::PARAM_TYPE_BOOL){
            return (bool) $value;
        }
        else if($type == self::PARAM_TYPE_OBJECT){
            return (object) $value;
        }
        else{
            return (string) $value;
        }
    }

    /**
     * gets a GET param
     *
     * @param string $key
     * @param mixed $default the value if key not found
     * @param mixed $type type casting
     * @return mixed
     */
    public function get($key, $default = null, $type = self::PARAM_TYPE_STRING){
        if(array_key_exists($key, $this->params)){
            return $this->_typeCast($this->params[$key], $type);
        }
        return $default;
    }
    
    /**
     * gets a GET param and convert to int
     *
     * @param string $key
     * @param mixed $default the value if key not found     
     * @return mixed
     */
    public function getInt($key, $default = null){
        if(array_key_exists($key, $this->params)){
            return $this->_typeCast($this->params[$key], self::PARAM_TYPE_INT);
        }
        return $default;
    }

    /**
     * gets a POST param
     *
     * @param string $key
     * @param mixed $default the value if key not found
     * @param mixed $type type casting
     * @return mixed
     */
    public function post($key, $default = null, $type = self::PARAM_TYPE_STRING){
        if(isset($_POST[$key])){
            return $this->_typeCast($_POST[$key], $type);
        }
        return $default;
    }
    
    public function getMethod(){
        return $_SERVER['REQUEST_METHOD'];
    }
    
    public function isPost(){
        return ($this->getMethod() == 'POST');
    }
    
    public function isGet(){
        return ($this->getMethod() == 'GET');
    }
    
    public function isPut(){
        return ($this->getMethod() == 'PUT');
    }
    
    public function isDelete(){
        return ($this->getMethod() == 'DELETE');
    }    
    
    public function isSSL(){
        return $this->_isSSL;
    }
    
    public function getHeader($name){
        $name = 'HTTP_'.strtoupper(str_replace('-', '_', $name));
        if(isset($_SERVER[$name])){
            return $_SERVER[$name];
        }
        return null;
    }
    
    public function getHeaders(){
        if(function_exists('apache_request_headers')){
            return apache_request_headers();
        }
        $out = array();
        foreach($_SERVER as $key=>$value){
            if (substr($key, 0, 5) == 'HTTP_'){ 
                $key = str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', substr($key, 5)))));
                $out[$key] = $value;
            }
        }
        return $out;
    }
    
    public function getAuthData(){
        if(isset($_SERVER['PHP_AUTH_USER'])){            
            return array(
                'user' => $_SERVER['PHP_AUTH_USER'],
                'pw' => $_SERVER['PHP_AUTH_PW'],
                'type' => 'basic'
            );
        }
        else if(isset($_SERVER['PHP_AUTH_DIGEST'])){
            // see http://de.php.net/manual/en/features.http-auth.php
            $data = $this->httpDigestParse($_SERVER['PHP_AUTH_DIGEST']);
            $data['type'] = 'digest';
            return $data;
        }
        else if(isset($_SERVER['HTTP_AUTHORIZATION'])){
            $data = explode(':', base64_decode(substr($_SERVER['HTTP_AUTHORIZATION'], 6)));
            return array(
                'user' => $data[0],
                'pw' => $data[1],
                'type' => 'basic'
            );
        }
        return null;
    }
    
    function httpDigestParse($txt){        
        $needed_parts = array('nonce'=>1, 'nc'=>1, 'cnonce'=>1, 'qop'=>1, 'username'=>1, 'uri'=>1, 'response'=>1);
        $data = array();
        $keys = implode('|', array_keys($needed_parts));        
        preg_match_all('@('.$keys.')=(?:([\'"])([^\2]+?)\2|([^\s,]+))@', $txt, $matches, PREG_SET_ORDER);
        foreach ($matches as $m){
            $data[$m[1]] = $m[3] ? $m[3] : $m[4];
            unset($needed_parts[$m[1]]);
        }
        return $needed_parts ? false : $data;
    }
 
    public function __toString(){
        $array = array(
            'controllerName'=>$this->controllerName,
            'actionName'=>$this->actionName,
            'params'=>$this->params
        );
        
        $out = '<pre>';
        $out .= print_r($array, true);
        $out .= '</pre>';
        return $out;
    }

    /**
     * saves the referrer in session
     */
    public function setReferrer(){
        RedMVC\Session::getInstance()->referrer = array(
            'controller'    => $this->controllerName,
            'action'        => $this->actionName,
            'params'        => $this->params,
            'requestString' => $this->requestString
        );
    }
    
    
}